package com.github.springtestdbunit;
import java.lang.reflect.Field;

import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
import com.github.springtestdbunit.DbUnitRule;
import com.github.springtestdbunit.DbUnitRule.DbUnitTestContextAdapter;
/**
 * Workaround DbUnitRule swallowing test errors issue
 * (https://github.com/springtestdbunit/spring-test-dbunit/issues/26).
 * A patch has been provided for this issue but it has not been integrated/released yet.
 */
public class MyDbUnitRule extends DbUnitRule {
    @Override
    public Statement apply(Statement base, FrameworkMethod method, Object target)  {
        DbUnitTestContextAdapter context = new DbUnitTestContextAdapter(method, target);
        // return our fixed Statement implementation
        return new MyStatement(context, base);
    }

    private DbUnitRunner getRunner() throws Exception {
        // As the runner is a private field in DbUnitRule, we use reflection to get access
        Field runner = DbUnitRule.class.getDeclaredField("runner");
        runner.setAccessible(true);
        return (DbUnitRunner) runner.get(this);
    }

    /**
     * Fixed implementation of DbUnitStatement: the evaluate method will throw any test exception.
     */
    private class MyStatement extends Statement  {
        private Statement nextStatement;
        private DbUnitTestContextAdapter testContext;

        public MyStatement(DbUnitTestContextAdapter testContext, Statement nextStatement) {
            this.testContext = testContext;
            this.nextStatement = nextStatement;
        }

        @Override
        public void evaluate() throws Throwable {
            DbUnitRunner runner = getRunner();
            runner.beforeTestMethod(this.testContext);
            try {
                this.nextStatement.evaluate();
            } catch (Throwable e) {
                this.testContext.setTestException(e);
            }
            runner.afterTestMethod(this.testContext);

            if (testContext.getTestException() != null) {
                throw testContext.getTestException();
            }
        }
    }
}